import React, { useEffect, useRef, MutableRefObject, ComponentType } from "react";
import { 
    BarData,
    ChartOptions,
    createChart,
    DeepPartial,
    IChartApi,
    LineSeriesPartialOptions,
    LineStyle,
    SeriesMarker,
    Time
} from "lightweight-charts";
import {
    ExtremumType,
    Extremum,
    Side,
    ChartOrder,
    ChartPosition,
} from './ui.model';
import {
    CHART_WIDTH,
    CHART_HEIGHT
} from '../../../constants'

export interface PriceChartProps {
    candles: BarData[];
    extremums: Extremum[];
    positions: ChartPosition[];
}

const chartOptions: DeepPartial<ChartOptions> = {
    width: CHART_WIDTH,
    height: CHART_HEIGHT,
    timeScale: {timeVisible: true}
};

const supportExtremumOptions: LineSeriesPartialOptions = {
    color: 'rgb(38, 119, 218)',
    lineWidth: 1,
    lastValueVisible: false,
    priceLineVisible: false
};

const resistenceExtremumOptions: LineSeriesPartialOptions = {
    color: 'rgb(218, 38, 65)',
    lineWidth: 1,
    lastValueVisible: false,
    priceLineVisible: false
};

const createBuyMarker = (time: number): SeriesMarker<Time> => ({
    time: time / 1000 as Time,
    position: 'belowBar',
    color: '#39c42f',
    shape: 'arrowUp',
    text: 'Buy'
});

const createSellMarker = (time: number): SeriesMarker<Time> => ({
    time: time / 1000 as Time,
    position: 'aboveBar',
    color: '#db0b3f',
    shape: 'arrowDown',
    text: 'Sell'
});

const drawMarker = (chart: IChartApi) => (order: ChartOrder) => {
    const series = chart.addLineSeries({
        color: 'rgba(38, 198, 218, 0)',
        lastValueVisible: false
    });
    series.setData([{
        time: order.time / 1000 as Time,
        value: order.price
    }]);

    const markers: SeriesMarker<Time>[] = [];
    const marker = order.side === Side.buy 
    ? createBuyMarker(order.time) 
    : createSellMarker(order.time);

    markers.push(marker);
    series.setMarkers(markers);
};

const drawStopLoss = (chart: IChartApi, position: ChartPosition): void => {
    const stoplossSeries = chart.addLineSeries({
        color: 'rgb(218, 122, 38)',
        lineWidth: 2,
        lastValueVisible: false,
        priceLineVisible: false
    });

    stoplossSeries.setData([
        {
            time: position.in[0].time / 1000 as Time,
            value: position.stopLoss
        },
        {
            time: position.in[0].time / 1000 + 100000000 as Time,
            value: position.stopLoss 
        }
    ]);
};

const drawPositionLinkLine = (chart: IChartApi, position: ChartPosition): void => {
    if(position.out.length > 0) {
        const linkSeries = chart.addLineSeries({
            color: 'rgb(38, 218, 41)',
            lineWidth: 2,
            lineStyle: LineStyle.Dashed,
            lastValueVisible: false,
            priceLineVisible: false
        });
        linkSeries.setData([
            {
                time: position.in[0].time / 1000 as Time,
                value: position.in[0].price
            },
            {
                time: position.out[0].time / 1000 as Time,
                value: position.out[0].price 
            }
        ]);
    }
};

const drawPosition = (chart: IChartApi, position: ChartPosition): void => {
    position.in.forEach(drawMarker(chart));
    position.out.forEach(drawMarker(chart));

    drawStopLoss(chart, position);
    drawPositionLinkLine(chart, position);
};

const drawPositions = (chart: IChartApi, positions: ChartPosition[]): void => {
    positions.forEach((position) => drawPosition(chart, position));
};

const drawCandles = (chart: IChartApi, candles: BarData[]): void => {
    chart.addCandlestickSeries().setData(candles);
};

const drawExtremum = (chart: IChartApi, extremum: Extremum): void => {
    const options = extremum.type === ExtremumType.support
    ? supportExtremumOptions
    : resistenceExtremumOptions;

    const point1 = {
        time: extremum.time / 1000 as Time,
        value: extremum.price
    };

    const point2 = {
        time: extremum.time / 1000 + 100000000 as Time,
        value: extremum.price
    };

    chart
    .addLineSeries(options)
    .setData([point1, point2]);
};

const drawExtremums = (chart: IChartApi, extremums: Extremum[]): void => {
    extremums.forEach((extremum) => drawExtremum(chart, extremum));
};

const initChart = (ref: MutableRefObject<HTMLDivElement>): IChartApi => {
    const domId = ref.current;
    return createChart(domId, chartOptions);
};

const PriceChart: ComponentType<PriceChartProps> = ({candles, extremums, positions}) => {
    const myRef = useRef() as MutableRefObject<HTMLDivElement>;

    useEffect(() => {
        const chart = initChart(myRef);
        drawCandles(chart, candles);
        drawExtremums(chart, extremums);
        drawPositions(chart, positions);

        return () => chart.remove();
    });

    return (
        <div ref={myRef} />
    )
};

export default PriceChart;